package mpbft

import (
	"blockchain/public/entity"
	"fmt"
	"math"
)
const N=entity.N //节点数
const K=entity.K //主节点数
const BlockSize=entity.BlockSize
const TradeSize=entity.TradeSize
const alpha = 2
const prior =0.4
const clientSeq=499
var currentRound=0
var currentTradeSeq=0
var trades [10000]entity.Trade
var node [N] entity.Node
var primarysSeq [K]int
var replicasSeq [N-K]int
var initialNode=false
var TradeSet [N][K*TradeSize] entity.Trade
var chanPriRecRequest=make([]chan requestMessage,N)
var chanNodeRecPre_prepare=make([]chan pre_prepareMessage,N)
var FlagPre_prepared=make([][]chan bool,N,N)
var chanPriRecPrepare1=make([]chan prepare1Message,N)
var FlagPrepared1=make([][]chan bool,N,N)
var chanNodeRecPrepare2=make([]chan prepare2Message,N)
var FlagPrepared2=make([][]chan bool,N,N)
var chanPriRecCommit1=make([]chan commit1Message,N)
var FlagCommitted1=make([][]chan bool,N,N)
var chanNodeRecCommit2=make([]chan commit2Message,N)
var FlagCommitted2=make([][]chan bool,N,N)
var chanReply=make(chan commit2Message,K)
var FlagReplied=make([]chan bool,N)
var blockchain[600] entity.Block
var sumrounds=entity.SumRounds

func Main(){
	fmt.Printf("改进的多主节点共识IMPBFT:N=%d,K=%d,R=%d",N,K,sumrounds)
	for currentRound=0;currentRound<sumrounds;currentRound++{
		request()
		chanNodeRecPre_prepareInitial()
		chanPriRecPrepare1Initial()
		chanNodeRecPrepare2Initial()
		chanPriRecCommit1Initial()
		chanNodeRecCommit2Initial()
		flagInitial()
		for i:=range primarysSeq{
			go primaryConsensus(primarysSeq[i])
		}
		for i:=range replicasSeq{
			go replicaConsensus(replicasSeq[i])
		}
		reply()
	}
}

func flagInitial() {
	for i:=range primarysSeq {
		FlagPre_prepared[primarysSeq[i]]= make([]chan bool, N)
	}
	for i:=range primarysSeq {
		for j:=0;j<N;j++{
			FlagPre_prepared[primarysSeq[i]][j]=make(chan bool,1)
		}
	}
	for i:=0;i<N;i++{
		FlagPrepared1[i]=make([]chan bool,N)
	}
	for i:=0;i<N;i++{
		for j:=0;j<N;j++{
			FlagPrepared1[i][j]=make(chan bool,1)
		}
	}

	for i:=range primarysSeq {
		FlagPrepared2[primarysSeq[i]]= make([]chan bool, N)
	}
	for i:=range primarysSeq {
		for j:=0;j<N;j++{
			FlagPrepared2[primarysSeq[i]][j]=make(chan bool,1)
		}
	}

	for i:=0;i<N;i++{
		FlagCommitted1[i]=make([]chan bool,N)
	}
	for i:=0;i<N;i++{
		for j:=0;j<N;j++{
			FlagCommitted1[i][j]=make(chan bool,1)
		}
	}

	for i:=range primarysSeq {
		FlagCommitted2[primarysSeq[i]]= make([]chan bool, N)
		FlagReplied[primarysSeq[i]]=make(chan bool,1)
	}
	for i:=range primarysSeq {
		for j:=0;j<N;j++{
			FlagCommitted2[primarysSeq[i]][j]=make(chan bool,1)
		}
	}
	chanReply=make(chan commit2Message,K)
}


func primaryConsensus(priSeq int){
	pre_prepare(priSeq)
	for i:=range primarysSeq{
		<-FlagPre_prepared[primarysSeq[i]][priSeq]
	}
	close(chanNodeRecPre_prepare[priSeq])
	prepare1(priSeq)
	for i:=0;i<N;i++{
		<-FlagPrepared1[i][priSeq]
	}
	close(chanPriRecPrepare1[priSeq])
	prepare2(priSeq)
	for i:=range primarysSeq {
		<-FlagPrepared2[primarysSeq[i]][priSeq]
	}
	close(chanNodeRecPrepare2[priSeq])
	commit1(priSeq)
	for i:=0;i<N;i++{
		<-FlagCommitted1[i][priSeq]
	}
	close(chanPriRecCommit1[priSeq])
	commit2(priSeq)
	for i:=range primarysSeq {
		<-FlagCommitted2[primarysSeq[i]][priSeq]
	}
	close(chanNodeRecCommit2[priSeq])
}
func replicaConsensus(nodeSeq int){
	for i:=range primarysSeq{
		<-FlagPre_prepared[primarysSeq[i]][nodeSeq]
	}
	close(chanNodeRecPre_prepare[nodeSeq])
	prepare1(nodeSeq)
	for i:=range primarysSeq {
		<-FlagPrepared2[primarysSeq[i]][nodeSeq]
	}
	close(chanNodeRecPrepare2[nodeSeq])
	commit1(nodeSeq)
	for i:=range primarysSeq {
		<-FlagCommitted2[primarysSeq[i]][nodeSeq]
	}
	close(chanNodeRecCommit2[nodeSeq])
}


func chanNodeRecPre_prepareInitial(){
	for i:=0;i<N;i++{
		chanNodeRecPre_prepare[i]=make(chan pre_prepareMessage,K)
	}
}

func chanPriRecPrepare1Initial(){
	for i:=range primarysSeq{
		chanPriRecPrepare1[primarysSeq[i]]=make(chan prepare1Message,N*2/3+1)
	}
}

func chanNodeRecPrepare2Initial(){
	for i:=0;i<N;i++{
		chanNodeRecPrepare2[i]=make(chan prepare2Message,K)
	}
}

func chanPriRecCommit1Initial(){
	for i:=range primarysSeq{
		chanPriRecCommit1[primarysSeq[i]]=make(chan commit1Message,N*2/3+1)
	}
}

func chanNodeRecCommit2Initial(){
	for i:=0;i<N;i++{
		chanNodeRecCommit2[i]=make(chan commit2Message,K)
	}
}


func setVaildTurn(){
	for i:=0;i<N;i++{
		node[i].VaildTurn = math.Pow(float64(alpha),float64(node[i].ByzantineTurnNum))*float64(node[i].TurnNum)+float64(node[i].PriorNum)
	}
}

func priorNodeSet()  {
	for i:=0;i<K;i++{
		node[i].PriorNum=prior
	}
	for i:=0;i<K;i++{
		node[i].TurnNum=0
		node[i].ByzantineTurnNum=0
	}
	setVaildTurn()
	initialNode=true
}

func updatePrimarys(){
	if initialNode{
		setVaildTurn()
		var nodeSeq [K] int
		for i:=0;i<K;i++{
			max:=0.0
			maxSeq :=0
			for j:=0;j<N;j++{
				if node[j].VaildTurn>max {
					maxSeq=j
					max=node[j].VaildTurn
				}
			}
			nodeSeq[i]=maxSeq
			node[maxSeq].VaildTurn=-1.0
		}
		primarysSeq =nodeSeq
		backupsseqJ :=0
		for i:=0;i<N;i++{
			if node[i].VaildTurn !=-1.0{
				replicasSeq[backupsseqJ]=i
				backupsseqJ++
			}
		}
		setVaildTurn()
	}else{
		priorNodeSet()
		updatePrimarys()
	}
}
